Code instrumentation is a method for analyzing and evaluating program code performance. In one approach to code instrumentation, new instructions (or probe code) are added to the program, and, consequently, the original code in the program is changed and/or relocated. Some examples of probe code include adding values to a register, moving the content of one register to another register, moving the address of some data to some registers, etc. The changed and/or relocated code is referred to as instrumented code or, more generally, as an instrumented process. For purposes of the present discussion, instrumented code is one type of dynamically generated code. Although the following discussion explicitly recites and discusses code instrumentation, such discussion and examples are for illustration only. That is, the following discussion also applies to various other types of dynamically generated code.
One specific type of code instrumentation is referred to as dynamic binary instrumentation. Dynamic binary instrumentation allows program instructions to be changed on-the-fly. Measurements such as basic-block coverage and function invocation counting can be accurately determined using dynamic binary instrumentation. In some instances, dynamic binary instrumentation, as opposed to static instrumentation, is performed at program run-time and only instruments those parts of an executable that are actually executed. This minimizes the overhead imposed by the instrumentation process itself. Furthermore, performance analysis tools based on dynamic binary instrumentation require no special preparation of an executable such as, for example, a modified build or link process.
A typical prior art code instrumentation process implements dynamic binary instrumentation and analysis by compiling the source code of a target application (e.g., the application being analyzed) with a specific instrumentation option enabled. This option results in the application code being compiled and instrumented with probe code to facilitate analysis. The probe code is a sequence of instructions used to collect different metrics of the profiled application code. When the resulting instrumented application code is executed, profile data generated by the inserted probe code is collected in a file for later analysis. This instrumentation process does not require any prior modification to the application code. The profile data is then examined and used to create reports depicting the execution flow of the application code.
Current dynamic profiling tools combine dynamic binary instrumentation and periodic sampling to produce a callgraph for application code which has not been compiled/linked with any special options. The callgraph consists of entries for associated functions sorted according to the total time of their descendants and themselves. Every entry in the callgraph is accompanied by data that shows the number of times a function was called by its associated parent(s) and the number of times the function called its associated children.
Generally, to compile such data in the callgraph during the code execution the instrumentation process collects information such as sample counts, arc counts, and function counts. The sample counts include collecting instruction pointer samples, which are also referred to as a program counter samples, at regular intervals during the code execution of a profiled application. These instruction pointer samples are then processed to obtain an estimate of the amount of time taken at different functions during the code execution.
The arc count consists of a source address, a target address and a count representing the number of times this arc was executed. Only those arcs for which the target addresses form function entry points are considered to collect arc count values. The collected arc count values are then processed to determine the parents (callers) and children (callees) for every function in the profiled application code.
The function count consists of a function start address and a count value. The count value represents the number of times the function was executed in the profiled application code. In the current instrumentation process the callgraph is generated using static instrumentation and the function count for a function is not collected using the probe code, but is calculated by adding up the counts of all the call arcs for which this function formed the target. This type of callgraph generation according to the current instrumentation process is error prone since if for any reason the source function cannot not be instrumented, then there would not be any arcs from the source function to a target function. This can result in excluding the number of arcs from that source function in the total function count of the target function. This can in-turn result in an incorrect callgraph.
In addition, calculation of function counts of functions with multiple entry points, which can start executing from more than one single address, requires every entry point to be treated as a separate function and that the probe code be placed at every function entry point during the instrumentation process. Generally, for callgraph generation, since different function entry points are considered as separate functions and only the call arcs are collected, the point non-call arcs which contribute to the time propagated from one function entry point to another can get missed out and result in an incorrect callgraph.